/*
 * @Author: czy
 * @Date: 2021-04-07 17:16:03
 * @LastEditTime: 2021-05-28 16:48:01
 */
#include "const.h"
#include "string.h"

/**
 * @description: Set the character array to zero.
 * @param str   The first address of the character array.
 * @param len   The length of the character array.
 */
void strzero(char *str, int len)
{
    for (int i = 0; i < len; i++)
        str[i] = '\0';
}

/**
 * @description: Compare two strings.
 * @param str1  String1.
 * @param str2  String2.
 * @return 0 equal; other unequal.
 */
int strcmp(char *str1, char *str2)
{
    while ((*str1) && (*str1 == *str2))
    {
        str1++;
        str2++;
    }

    if (*(unsigned char *)str1 > *(unsigned char *)str2)
    {
        return 1;
    }
    else if (*(unsigned char *)str1 < *(unsigned char *)str2)
    {
        return -1;
    }
    else
    {
        return 0;
    }
}

/**
 * @description: Copy string.
 * @param dst   Destination string.
 * @param src   Source string.
 * @return The first address of the destination string.
 */
char *strcpy(char *dst, char *src)
{
    char *str = dst;
    while ((*str++ = *src++) != '\0')
        ;
    return dst;
}

/**
 * @description: Calculates the length of a string (do not count '\0' at the end).
 * @param str   The first address of the string.
 * @return Length of the string.
 */
int strlen(char *str)
{
    int len = 0;
    while (*(str++) != '\0')
    {
        len++;
    }
    return len;
}

/**
 * @description: Point the pointer to the end of the string.
 * @param str   The first address of the string.
 * @return The last address of the string (Point to '\0').
 */
char *strend(char *str)
{
    while (*str != '\0')
        str++;
    return str;
}

/**
 * @description: String expansion.
 * @param dst   The last address of the destination string (Point to '\0').
 * @param src   The first address of the source string.
 * @return The last address of the new string (Point to '\0').
 */
char *extend(char *dst, char *src)
{
    while (*src != '\0')
        *(dst++) = *(src++);
    *dst = '\0';
    return dst;
}

/**
 * @description: String concatenation.
 * @param dst   Destination string.
 * @param src   Source string.
 * @return The first address of the destination string.
 */
char *strcat(char *dst, char *src)
{
    extend(strend(dst), src);
    return dst;
}

/**
 * @description: Shift the whole string to the right by n bits, and the first n bits of the string are filled with ch.
 * @param str   The first address of the string.
 * @param n     The size of the shift.
 * @param ch    Filled characters.
 * @return The first address of the new string.
 */
char *strmove(char *str, int n, char ch)
{
    int length = strlen(str);
    char *p = str + length;
    while (p >= str)
    {
        *(p + n) = *p;
        p--;
    }
    for (int i = 0; i < n; i++)
    {
        *(str + i) = ch;
    }
    return str;
}

/**
 * @description: N-bit alignment. If relative is true, fill n bits altogether; else fill to n bits.
 * @param str   The first address of the string.
 * @param ch    Filled characters.
 * @param width The width of the fill.
 * @param align LEFT or RIGHT.
 * @param relative  TRUE or FALSE
 * @return The first address of the new string.
 */
char *fill(char *str, char ch, int width, int align, int relative)
{
    char *p;
    int count = relative ? width : width - strlen(str);
    if (count <= 0)
        return str;
    if (align == LEFT)
    {
        p = strend(str);
        while (count-- > 0)
            *(p++) = ch;
        *p = '\0';
    }
    else
    {
        strmove(str, count, ch);
    }
    return str;
}

/**
 * @description: Removes the specified character from the string.
 * @param dst   Destination string.
 * @param src   Source string.
 * @param ch1   Characters to be removed.
 * @param ch2   Flag character.
 * @return The first address of the new string.
 */
char *remove(char *dst, char *src, char ch1, char ch2)
{
    char *p1, *p2 = dst;
    while (*src != ch2 && *src != '\0')
        *(dst++) = *(src++);
    p1 = src;
    while (*(--dst) == ch1)
        *dst = '\0';
    dst++;
    while (*p1 != '\0')
        *(dst++) = *(p1++);
    return p2;
}

/**
 * @description: Determine whether a specific string contains a character.
 * @param str   The first address of the string.
 * @param ch    Character.
 * @return TRUE if find, else FALSE.
 */
int find(char *str, char ch)
{
    for (int i = 0; str[i] != '\0'; i++)
        if (str[i] == ch)
            return TRUE;
    return FALSE;
}

/**
 * @description: String reverse.
 * @param str   The first address of the string.
 */
void reverse(char *str)
{
    int ilen = strlen(str);
    char *p = str + ilen - 1;
    int itemp = 0;
    while (str < p)
    {
        itemp = *p;
        *p = *str;
        *str = itemp;
        p--;
        str++;
    }
}

/**
 * @description: Adds several characters to the end of the string.
 * @param str   The first address of the string.
 * @param ch    Character.
 * @param n     Number of character repetitions.
 * @return The first address of the new string.
 */
char *strappend(char *str, char ch, int n)
{
    char *p = strend(str);
    for (int i = 0; i < n; i++)
    {
        *p++ = ch;
    }
    *p = '\0';
    return str;
}

/**
 * @description: Pops several characters from the end of the string.
 * @param str   The first address of the string.
 * @return The first address of the new string.
 */
char *strpop(char *str, int n)
{
    char *p = strend(str);
    int length = strlen(str);
    n = n < length ? n : length;
    for (int i = 0; i < n; i++)
    {
        *(--p) = '\0';
    }
    return str;
}

/**
 * @description: Inserts a character at the specified position in the string.
 * @param str   The first address of the string.
 * @param index Insert position.
 * @param ch    Character.
 * @return The first address of the new string.
 */
char *strinsert(char *str, int index, char ch)
{
    char *p = str;
    int length = strlen(str);
    index = index < length ? index : length;
    for (int i = 0; i < index; i++)
    {
        p++;
    }
    fill(p, ch, 1, RIGHT, TRUE);
    return str;
}

/**
 * @description: Removes a character from the specified position of the string.
 * @param str   The first address of the string.
 * @param index Insert position.
 * @return The first address of the new string.
 */
char *strdelete(char *str, int index)
{
    char *p = str;
    int length = strlen(str);
    index = index < length ? index : length;
    for (int i = 0; i < index; i++)
    {
        p++;
    }
    while (*p != '\0')
    {
        *p = *(p + 1);
        p++;
    }
    return str;
}

/**
 * @description: Determine whether the character is a space.
 * @param ch    Character.
 * @return TRUE if it is a space, else FALSE.
 */
int is_white(int ch)
{
    return ' ' == ch || '\t' == ch || '\v' == ch || '\f' == ch || '\n' == ch || '\r' == ch;
}

/**
 * @description: Remove extra spaces from the string.
 * @param str   The first address of the string.
 * @return Number of spaces at the beginning.
 */
int deblank(char *str)
{
    int n = 0;
    char *p = str;
    char *src = str;
    char *dest = str;
    while ((is_white(*src)) && *src != '\0')
    {
        *src++;
        n++;
    }
    while (*src != '\0')
    {
        if (is_white(*src))
        {
            *dest++ = ' ';
            src++;
            while (is_white(*src))
            {
                src++;
            }
        }
        else
        {
            *dest++ = *src++;
        }
    }
    if (*(dest - 1) == ' ')
    {
        *(dest - 1) = '\0';
    }
    else
    {
        *dest = '\0';
    }
    return n;
}

/**
 * @description: Return the lowest index in str2 where substring str1 is found.
 * @param str1  String1.
 * @param str2  String2.
 * @return The lowest index in str2 where substring str1 is found. (return -1 if not found.)
 */
int strindex(char *str1, char *str2)
{
    char *p1 = str1, *p2 = str2, *p3 = str2;
    int index = 0;
    while (*p3 != '\0')
    {
        p2 = p3;
        while ((*p1) && (*p1 == *p2))
        {
            p1++;
            p2++;
        }
        if (*p1 == '\0')
        {
            return index;
        }
        else
        {
            index++;
            p3++;
        }
    }
    return -1;
}

/**
 * @description: Split a string by a given character.
 * @param dst   First address of Destination string array.
 * @param src   Source string.
 * @param delim Delimiter.
 * @return The length of the string array.
 */
int split(char **dst, char *src, char *delim)
{
    int n = 0;
    char *p1 = delim, *p2 = src, *p3 = src, *p4 = src;
    int length = strlen(delim);
    while (*p3 != '\0')
    {
        p1 = delim;
        p2 = p3;
        while ((*p1) && (*p1 == *p2))
        {
            p1++;
            p2++;
        }
        if (*p1 == '\0')
        {
            p3 = p2;
            for (int i = 0; i < length; i++)
            {
                *(--p2) = '\0';
            }
            dst[n++] = p4;
            p4 = p3;
        }
        else
        {
            p3++;
        }
    }
    dst[n++] = p4;
    return n;
}

/**
 * @description: Converts a string to an integer.
 * @param str   The first address of the string.
 * @return Conversion results.
 */
int str2int(char *str)
{
    int num = 0;
    int symbol = 1;
    if (*str == '-')
    {
        symbol = -1;
        str++;
    }
    while (*str != '\0')
    {
        if (*str >= '0' && *str <= '9')
        {
            num = num * 10 + *str - '0';
        }
        str++;
    }
    return num * symbol;
}

/**
 * @description: Converts an integer to a string.
 * @param str   The first address of the string.
 * @param num   Number.
 * @return Conversion results.
 */
char *int2str(char *str, int num)
{
    char temp[10] = "";
    int i = 0;
    if (num < 0)
    {
        str[0] = '-';
        num = -num;
    }
    while (num > 0)
    {
        temp[i++] = num % 10 + '0';
        num /= 10;
    }
    reverse(temp);
    strcat(str, temp);
    return str;
}

/**
 * @description: Concatenates multiple strings and joins them with the specified connector.
 * @param dst       Destination string
 * @param src       Source string array.
 * @param len       Length of source string array
 * @param connector Connector.
 * @return The first address of the new string.
 */
char *strjoin(char *dst, char **src, int len, char *connector)
{
    strcpy(dst, src[0]);
    for (int i = 1; i < len; i++)
    {
        strcat(dst, connector);
        strcat(dst, src[i]);
    }
    return dst;
}

/**
 * @description: Return a copy with all occurrences of substring old replaced by new.
 * @param dst       Destination string.
 * @param src       Source string.
 * @param oldstr    Old substring.
 * @param newstr    New substring.
 * @return The first address of the destination string.
 */
char *strreplace(char *dst, char *src, char *oldstr, char *newstr)
{
    char *temp[100];
    int length = split(temp, src, oldstr);
    strjoin(dst, temp, length, newstr);
    return dst;
}

/**
 * @description: Intercepts a substring from the source string.
 * @param dst   Destination string.
 * @param src   Source string.
 * @param start Substring start position.
 * @param end   Substring end position, -1 for the end of source string.
 * @return The first address of the destination string.
 */
char *substring(char *dst, char *src, int start, int end)
{
    char *p = dst;
    for (int i = 0; i < start && *src != '\0'; i++)
    {
        src++;
    }
    int i = start;
    while ((end < 0 || i < end) && *src != '\0')
    {
        *p++ = *src++;
        i++;
    }
    *p = '\0';
    return dst;
}

/**
 * @description: Copy memory.
 * @param dst   Destination address.
 * @param src   Source address.
 * @param n     Number of bytes.
 * @return The first address of the destination address.
 */
void *memcpy(void *dst, const void *src, unsigned n)
{
    char *dsts = (char *)dst;
    char *srcs = (char *)src;
    while (n--)
    {
        *dsts++ = *srcs++;
    }
    return dst;
}

/**
 * @description: Move memory.
 * @param dst   Destination address.
 * @param src   Source address.
 * @param n     Number of bytes.
 * @return The first address of the destination address.
 */
void *memmove(void *dst, const void *src, unsigned n)
{
    char *dsts = (char *)dst;
    char *srcs = (char *)src;
    if (dsts > srcs && dsts < srcs + n)
    {
        srcs += n;
        dsts += n;
        while (n--)
        {
            *(--dsts) = *(--srcs);
        }
    }
    else
    {
        while (n--)
        {
            *(dsts++) = *(srcs++);
        }
    }
    return dst;
}

/**
 * @description: Set the memory.
 * @param src   The first address of the memory block.
 * @param ch    Character.
 * @param n     Number of bytes.
 */
void *memset(void *src, int ch, unsigned n)
{
    void *ret = src;
    while (n--)
    {
        *(char *)src = ch;
        src = (char *)src + 1;
    }
    return ret;
}

/**
 * @description: Compare two blocks of memory.
 * @param buf1  Memory block1.
 * @param buf2  Memory block2.
 * @return 0 equal; other unequal.
 */
int memcmp(const void *buf1, const void *buf2, unsigned n)
{
    char *str1 = (char *)buf1;
    char *str2 = (char *)buf2;
    while ((*str1) && (*str1 == *str2))
    {
        str1++;
        str2++;
    }
    if (*(unsigned char *)str1 > *(unsigned char *)str2)
    {
        return 1;
    }
    else if (*(unsigned char *)str1 < *(unsigned char *)str2)
    {
        return -1;
    }
    else
    {
        return 0;
    }
}